package com.platform.controller;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.common.collect.Maps;
import com.platform.asserts.ArgumentAssert;
import com.platform.base.Constants;
import com.platform.base.R;
import com.platform.base.Tree;
import com.platform.base.mvc.BaseController;
import com.platform.context.SSOUserContext;
import com.platform.dto.Modular;
import com.platform.dto.Right;
import com.platform.dto.Role;
import com.platform.dto.SysLogLoginDTO;
import com.platform.dto.User;
import com.platform.exception.BizException;
import com.platform.ext.cache.utils.RedisCache;
import com.platform.logger.annotation.ApiLog;
import com.platform.logger.cons.Constant;
import com.platform.logger.properties.PlatformLoggerProperties;
import com.platform.logger.provider.LogServerInfoProvider;
import com.platform.logger.provider.SpringAware;
import com.platform.logger.util.NetUtil;
import com.platform.logger.util.StrUtils;
import com.platform.logger.util.WebUtil;
import com.platform.security.Aes;
import com.platform.security.CipherUtil;
import com.platform.service.IModular;
import com.platform.service.IRight;
import com.platform.service.IRole;
import com.platform.service.IRoleRight;
import com.platform.service.IUser;
import com.platform.service.SysLogLoginService;
import com.platform.utils.ArrayUtils;
import com.platform.utils.HttpContextUtils;
import com.platform.utils.StringUtils;
import com.platform.utils.TokenManager;

import cn.hutool.core.map.MapUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

@Controller
@Api(tags="COMMON-用户登录验证服务模块")
@RequestMapping("sys")
public class IndexController extends BaseController{
	
	@Autowired
	private RedisCache redisCache;
	
	@Autowired
	private IUser userServices;
	@Autowired
	private IRight rightService;
	@Autowired
	private IModular modularService;
	@Autowired 
	private IRoleRight roleRightServices;
	@Autowired
	private IRole roleService;
	@Autowired
	private SysLogLoginService logLoginService;
	
	private final PlatformLoggerProperties properties;
	
	private final LogServerInfoProvider serverInfoProvider;
	
	public IndexController(PlatformLoggerProperties properties, LogServerInfoProvider serverInfoProvider) {
		super();
		this.properties = properties;
		this.serverInfoProvider = serverInfoProvider;
	}

	@RequestMapping(value="initConfig",method=RequestMethod.POST)
	@ResponseBody
	@ApiOperation(value = "获取系统配置信息")
	public Object initConfig(){
		Map<String,Object> obj = MapUtil.newHashMap();
		try {
			obj.put("SINGLEONLINE", true);
			obj.put("userKeyPlateLogin", false);
			obj.put("enableIM",true);
		} catch (Exception e) {
			obj.put("SINGLEONLINE", "true");
			obj.put("userKeyPlateLogin", "false");
			obj.put("enableIM", "false");
		}
    	return R.success(obj);
	}
	
	@ApiLog(value="系统模块",desc="获取当前用户信息")
	@RequestMapping(value="accountInfo",method=RequestMethod.POST)
	@ResponseBody
	@ApiOperation(value="获取登录用户基本信息",notes="获取登录用户基本信息")
	public Object getAccountInfo() {
		User user = userServices.get(TokenManager.getUserID(HttpContextUtils.getAccessToken()));
		Map<String,Object> accountInfo = redisCache.getCacheObject("platform:account:ACCOUNT_INFO_"+user.getId());
		if(accountInfo == null) {
			List<Right> rightList = rightService.findRightByUserCode(user.getId().toString());
			StringBuffer sourceRightSb = new StringBuffer();
			for(Right ri : rightList){
				sourceRightSb.append(ri.getRightcode());
				sourceRightSb.append(",");
			}
			accountInfo = Maps.newHashMap();
			accountInfo.put("accountInfo", user);
			accountInfo.put("accountSecretCode", user.getLoginname());
			accountInfo.put("secretcode", "platform");
			accountInfo.put("accountPriview", sourceRightSb);
			accountInfo.put("sourceRightSb", sourceRightSb);
			redisCache.setCacheObject("platform:account:ACCOUNT_INFO_"+user.getId(), accountInfo);
		}
		return R.success(accountInfo);
	}
	
	
	@RequestMapping(value="login",method=RequestMethod.POST)
	@ResponseBody
	@ApiOperation(value="用户登录")
	@ApiImplicitParams({
        @ApiImplicitParam(name = "loginName", value = "用户账号", required = true, dataType = "String"),
        @ApiImplicitParam(name = "password", value = "用户账号密码", required = true, dataType = "String"),
        @ApiImplicitParam(name = "captcha", value = "登录校验码", required = true, dataType = "String"),
        @ApiImplicitParam(name = "captchaseq", value = "校验码存储序号", required = true, dataType = "String")
	})
	public Object login(String loginName,String password,String captcha,String captchaseq){
		ArgumentAssert.notBlank(loginName,"请输入登录账号！");
		ArgumentAssert.notBlank(password,"请输入用户密码！");
		ArgumentAssert.notBlank(captcha,"验证码不能为空，请输入！");
		ArgumentAssert.notBlank(captchaseq,"缺少验证码校验码信息！");
		//验证校验码是否正确
		String cacheCaptcha = redisCache.getCacheObject("platform:captchaCode:captcha_"+captchaseq);
		ArgumentAssert.notBlank(cacheCaptcha,"验证码已失效");
		if(!captcha.equalsIgnoreCase(cacheCaptcha)) {
			return R.fail("验证码错误");
		}
		//校验用户
		User user = userServices.validateUserName(loginName);
		if(user != null){
			password = Aes.aesDecrypt(password);
			User userAndPwd = userServices.validateUserNameAndPwd(loginName, CipherUtil.generatePassword(password));
			if(userAndPwd != null) {
				BeanUtils.copyProperties(userAndPwd, user);
				//判断用户是否使用合法IP访问
				if(!StringUtils.isEmpty(user.getBindClientIp())) {
					String realIp = getRemortIP(request);
					if(!realIp.equals(user.getBindClientIp())) {
						return R.fail("您账号绑定的客户端地址是："+user.getBindClientIp()+"，请在指定电脑进行操作。如需变更，请联系管理员。");
					}
				}
				if(user.getJobstate().equals(3L) || user.getPasswordErrorNum() > 3){ //如果用户状态为锁定或密码输入三次错误账号将被锁定不能登录系统
					return R.fail("用户["+user.getLoginname()+"]已锁定，不能登录到系统中");
				}else if(user.getJobstate().equals(2L)){
					return R.fail("用户["+user.getLoginname()+"]已离职，不能登录到系统中");
				}else{
					//查询当前用户拥有的权限
					List<Right> rightList = rightService.findRightByUserCode(user.getId()+"");
					List<String> sourcesSb = new ArrayList<String>();
					for(Right ri : rightList){
						sourcesSb.add(ri.getRightcode());
					}
					ArrayUtils.removeDuplicate(sourcesSb);
					Map<String,Object> claims = MapUtil.newHashMap();
					claims.put(Constants.JWT_USER_NAME, user.getRealname());
					claims.put(Constants.JWT_USER_KEY, user.getId());//这里存放的是权限字信息
					claims.put("IP",getRemortIP(request));
					Role role = roleService.selectRolesByUserCode(user.getId()).get(0);
					
			        String token = TokenManager.getAccessToken(user.getId()+"", claims);
			        Map<String,String> data = MapUtil.newHashMap();
			        data.put("username", user.getLoginname());
			        data.put("userid",user.getId().toString());
			        data.put("LOGINED", "true");
			        data.put("realname",user.getRealname());
			        data.put("dept",user.getDeptname());
			        data.put("deptCode",user.getDeptcode().toString());
			        data.put("post",user.getPostText());
			        data.put("theme",user.getTheme());
			        data.put("token",token);
			        data.put("refreshToken", TokenManager.getRefreshToken(user.getId()+"", claims));
			        data.put("permissions", org.apache.commons.lang3.ArrayUtils.toString(sourcesSb));
			        data.put("role",role.getRolename());
			        redisCache.setCacheObject("platform:session:aCTh_"+user.getId(),data);
			        userServices.saveLastLoginTime(user.getId());
			        //保存登录日志
			        SysLogLoginDTO logLogin = new SysLogLoginDTO();
			        logLogin.setAppName(StrUtils.isBlank(SpringAware.getAppName())  ? properties.getDefaultAppName() : SpringAware.getAppName());
			        logLogin.setServerName(NetUtil.getHostName());
			        logLogin.setServerIp(serverInfoProvider.getIp());
			        logLogin.setAppPort(serverInfoProvider.getPort());
			        logLogin.setEnv(StrUtils.isBlank(SpringAware.getEnv()) ? properties.getDefaultEnv() : SpringAware.getEnv());
			        logLogin.setVisitorIp(WebUtil.getIP(request));
			        logLogin.setUserAgent(request.getHeader(Constant.USER_AGENT_HEADER));
			        logLogin.setLoginTime(new Date());
			        logLogin.setLoginUser(user.getLoginname());
			        logLoginService.insert(logLogin);
					return R.success(data,"用户["+user.getLoginname()+"]登录成功，正在为您跳转到首页");
				}
			}else{
				userServices.savePasswordErrorLastTime(user.getId());
				userServices.saveLoginErrorCount(user.getId());
				return R.fail("用户["+loginName+"]密码错误，请重新输入");
			}
		}else{
			return R.fail("用户["+loginName+"]不存在，请核对");
		}
	}
	
	@RequestMapping(value="refreshToken",method=RequestMethod.POST)
	@ResponseBody
	@ApiOperation(value="刷新令牌")
	public Object refreshToken(String accessToken) {
		if(!TokenManager.validateToken(accessToken)) {
			throw new BizException(1000, "令牌失效");
		}
		String userId = TokenManager.getUserID(accessToken);
		User user = userServices.get(userId);
		if(null == user) {
			throw new BizException(1000, "未能找到相关用户或令牌无效");
		}
		List<Right> rightList = rightService.findRightByUserCode(userId);
		List<String> sourcesSb = new ArrayList<String>();
		for(Right ri : rightList){
			sourcesSb.add(ri.getSourcescode());
		}
		ArrayUtils.removeDuplicate(sourcesSb);
		Map<String,Object> claims = MapUtil.newHashMap();
		claims.put(Constants.JWT_USER_NAME, user.getRealname());
		claims.put(Constants.JWT_USER_KEY, user.getId());
		claims.put(Constants.JWT_PERMISSIONS_KEY, sourcesSb);
        return R.success(TokenManager.getRefreshToken(user.getId()+"", claims));
	}
	
	@RequestMapping(value="logout",method=RequestMethod.POST)
	@ResponseBody
	@ApiOperation(value="退出当前登录")
    public Object logout() {
		//接入redis再实现
		Long userid = Long.valueOf(TokenManager.getUserID(HttpContextUtils.getAccessToken()));
		redisCache.deleteObject("platform:account:ACCOUNT_INFO_"+userid);
		redisCache.deleteObject("platform:resource:USER_MENUS_"+userid);
		redisCache.deleteObject("platform:resource:permissions:USER_PERMISSIONS_INFO_"+userid);
		redisCache.deleteObject("platform:session:aCTh_"+userid);
		SSOUserContext.clearContext();
        return R.success();
    }
	
	@RequestMapping(value="init",method=RequestMethod.GET)
	@ResponseBody
	@ApiOperation(value="初始化系统数据，用户登录成功后获取用户的操作菜单、权限等")
	public Object initMenu(){
		HttpServletResponse response = HttpContextUtils.getHttpServletResponse();
		Long userid = Long.valueOf(SSOUserContext.getContext().getId());
		List<Modular> menuList = redisCache.getCacheList("platform:resource:USER_MENUS_"+userid) ;
		try {
			if(menuList.isEmpty()) {
				userid = Long.valueOf(TokenManager.getUserID(HttpContextUtils.getAccessToken()));
				if(userid == null) {
					response.addHeader("sessionstatus", "timeout");
					PrintWriter writer = response.getWriter();
	                writer.print("{\"sessionstatus\":\"timeout\"}");
	                writer.flush();
	                writer.close();
	         		throw new BizException("登录超时");
				}
				List<Right> rightList = rightService.findRightByUserCode(userid.toString());
				List<String> sourcesSb = new ArrayList<String>();
				for(Right ri : rightList){
					sourcesSb.add(ri.getSourcescode());
				}
				ArrayUtils.removeDuplicate(sourcesSb);
				Map<String,Object> searchMenuParentMap = null;
				for(String parentCode : rightService.findSourceParent(userid.toString())){
					searchMenuParentMap = MapUtil.newHashMap();
					searchMenuParentMap.put("modularcode", parentCode);
					List<Modular> menuListFor = modularService.selectAll(searchMenuParentMap);
					for(Modular menu : menuListFor){
						menuList.add(menu);
					}
				}
				buildMenu(menuList,StringUtils.listToString(sourcesSb, ","));
				Collections.sort(menuList, new Comparator<Modular>(){
					@Override
					public int compare(Modular o1, Modular o2) {
						return o1.getOrder().compareTo(o2.getOrder());
					} 
				});
				redisCache.setCacheList("platform:resource:USER_MENUS_"+userid, menuList);
			}
			return menuList;
		}catch(Exception e) {//if this run has exception,logout system.
			e.printStackTrace();
			response.addHeader("sessionstatus", "timeout");
			Map<String,Object> failureMap = Maps.newHashMap();
			failureMap.put("sessionstatus","timeout");
			return failureMap;
		}
	}
	/**
	 * 递归构建系统菜单
	 * @param menuList
	 * @param rightList
	 */
	private void buildMenu(List<Modular> menuList,String rightList){
		List<Modular> childrensMenu = new ArrayList<Modular>();
		for(Modular menu : menuList){
			Map<String,Object> childmap = MapUtil.newHashMap();
			childmap.put("parent_", menu.getModularcode());
			List<Modular> sunMenu = modularService.selectAll(childmap);
			if(sunMenu != null && sunMenu.size() > 0){
				buildMenu(sunMenu,rightList);
			}else{
				menu.setLeaf(Boolean.TRUE);
		        if (rightList.contains(menu.getModularcode())) {
		          continue;
		        }
		        childrensMenu.add(menu);
		        continue;
			}
			menu.setChildren(sunMenu);
		}
		menuList.removeAll(childrensMenu);
	}
	
	@ApiLog(value="用户自主修改登录密码",desc="修改登录密码")
	@RequestMapping(value="modifyPwd",method=RequestMethod.POST)
	@ResponseBody
	@ApiOperation(value="修改账号登录密码")
	@ApiImplicitParams({
        @ApiImplicitParam(name = "sourcePwd", value = "账号原始密码", required = true, dataType = "String"),
        @ApiImplicitParam(name = "newPassword", value = "新密码", required = true, dataType = "String"),
        @ApiImplicitParam(name = "confimPwd", value = "确认新密码", required = true, dataType = "String")
	})
	public Object modifyPassword(String sourcePwd ,String newPassword,String confimPwd,HttpServletRequest request){
		ArgumentAssert.notBlank(sourcePwd,"账号原始密码不能为空");
		ArgumentAssert.notBlank(newPassword,"新密码不能为空");
		ArgumentAssert.notBlank(confimPwd,"确认密码不能为空");
		User userOld = userServices.get(TokenManager.getUserID(HttpContextUtils.getAccessToken()));
		if(newPassword.equals(confimPwd)){
			if(!userOld.getPassword().equals(CipherUtil.generatePassword(sourcePwd))){
				return R.fail("您输入的原登陆密码不对，请核对");
			}else{
				User user = new User();
				user.setId(userOld.getId());
				user.setPassword(newPassword);
				userServices.update(user);
				return R.success();
			}
		}else{
			return R.fail("两次输入的密码不一致请核对");
		}
	}
	
	@RequestMapping(value="authorizationModular",method=RequestMethod.POST)
	@ResponseBody
	@ApiOperation(value="根据角色、模块查询拥有的资源")
	public Object authorizationModular(String parentCode,String roleCode){
		List<Tree> treeList = new ArrayList<Tree>();
		StringBuffer rightSources = new StringBuffer();
		List<String> rightSourcesList = rightService.findSourceByRole(roleCode);
		for(String str : rightSourcesList){
			rightSources.append(str);
			rightSources.append(",");
		}
		Map<String,Object> searchMap = MapUtil.newHashMap();
    	searchMap.put("parent", StringUtils.isEmpty(parentCode)?"0":parentCode);
    	try {
			treeList = roleRightServices.authorizationModular(searchMap);
			modularListBuildRight(treeList,rightSources.toString());
			return R.success(treeList);
		} catch (BizException e) {
			return R.fail(e);
		}
	}
	
	private void modularListBuildRight(List<Tree> modulars,String sources) throws BizException{
    	List<Tree> modularList = new ArrayList<Tree>();
    	for(Tree m : modulars){
    		Map<String,Object> childrenMap = MapUtil.newHashMap();
    		childrenMap.put("parent", m.getSourcesCode());
    		List<Tree> childrenList = roleRightServices.authorizationModular(childrenMap);
    		if(childrenList != null && childrenList.size() > 0){
    			modularListBuildRight(childrenList,sources);
    		}else{
    			m.setLeaf(true);
    			if(m.getSourcesCode().equals("0001")){
    				modularList.add(m);
    			}
    			
    			Map<String,Object> rightMap = MapUtil.newHashMap();
    			rightMap.put("sourcesCode", m.getSourcesCode());
    			List<Tree> rightTreeNone = roleRightServices.queryRightByModular(rightMap);
    			if(rightTreeNone.size() == 0){
    			}else{
    				List<Tree> rightTree = new ArrayList<Tree>();
        			for(Tree tree : rightTreeNone){
        				tree.setChecked(sources.contains(tree.getSourcesCode()));
        				tree.setLeaf(Boolean.TRUE);
        				rightTree.add(tree);
        			}
    				m.setLeaf(false);
    				m.setChildren(rightTree);
    			}
    			
    			continue;
    		}
    		m.setChildren(childrenList);
    	}
    	modulars.removeAll(modularList);
    }
}
